#include "swpresourceeditor.h"

#include "../undo/graphicdatacommands.h"
#include "../utils/undomanager.h"
#include "items/graphicitemcommon.h"
#include "items/swpaggregationgraphicitem.h"
#include "items/swpartifactgraphicitem.h"
#include "items/swpassociationgraphicitem.h"
#include "items/swpcompositiongraphicitem.h"
#include "items/swpdependencygraphicitem.h"
#include "items/swpgeneralizationgraphicitem.h"
#include "items/swprealizationgraphicitem.h"
#include "items/swpresourcegraphicitem.h"
#include "items/swpworkergraphicitem.h"

#include <gallerycommon.h>

#include <QtCore/QMimeData>
#include <QtCore/QStringList>
#include <QtGui/QAction>
#include <QtGui/QDragEnterEvent>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QDropEvent>
#include <QtGui/QGraphicsView>

using namespace core;
using namespace gui;
using namespace swp;

const QString SwpResourceEditor::ACCEPTED_MIMETYPE =
    "application/x-swp-entitygraphicitem";
const QString SwpResourceEditor::ARTIFACT_MIMETYPE_DATA =
    "ARTIFACT_MIMETYPE_DATA";
const QString SwpResourceEditor::RESOURCE_MIMETYPE_DATA =
    "RESOURCE_MIMETYPE_DATA";
const QString SwpResourceEditor::WORKER_MIMETYPE_DATA =
    "WORKER_MIMETYPE_DATA";

SwpResourceEditor::SwpResourceEditor(QWidget *parent) :
    AbstractGraphicEditor(parent),
    m_addArtifactAction(0),
    m_addResourceAction(0),
    m_addWorkerAction(0),
    m_createAggregationAction(0),
    m_createAssociationAction(0),
    m_createCompositionAction(0),
    m_createDependencyAction(0),
    m_createGeneralizationAction(0),
    m_createRealizationAction(0),
    m_artifactMimeData(0),
    m_resourceMimeData(0),
    m_workerMimeData(0),
    m_artifactThumbnailDrag(),
    m_artifactThumbnailGallery(),
    m_resourceThumbnailDrag(),
    m_resourceThumbnailGallery(),
    m_workerThumbnailDrag(),
    m_workerThumbnailGallery(),
    m_aggregationThumbnailGallery(),
    m_aggregationThumbnailConnect(),
    m_associationThumbnailGallery(),
    m_associationThumbnailConnect(),
    m_compositionThumbnailGallery(),
    m_compositionThumbnailConnect(),
    m_dependencyThumbnailGallery(),
    m_dependencyThumbnailConnect(),
    m_generalizationThumbnailGallery(),
    m_generalizationThumbnailConnect(),
    m_realizationThumbnailGallery(),
    m_realizationThumbnailConnect()
{
    createThumbnails();
    createMimeData();
    createActions();
    connectSlots();

    m_view->viewport()->installEventFilter(this);
    m_view->setAcceptDrops(true);
}

SwpResourceEditor::~SwpResourceEditor()
{
    delete m_artifactMimeData;
    delete m_resourceMimeData;
    delete m_workerMimeData;
}

QStringList SwpResourceEditor::galleryGroups() const
{
    QStringList ret;

    ret << tr("Entities");
    ret << tr("Relations");

    return ret;
}

QList<GalleryItem> SwpResourceEditor::galleryItems(const QString& group) const
{
    QList<GalleryItem> ret;

    if (group == tr("Entities"))
    {
        GalleryItem artGalItem;
        artGalItem.Thumbnail = GalleryItemThumbnail(
            m_artifactThumbnailGallery, m_artifactThumbnailDrag);
        artGalItem.Label = tr("Artifact");
        artGalItem.Action = m_addArtifactAction;
        artGalItem.DragData = m_artifactMimeData;

        GalleryItem resGalItem;
        resGalItem.Thumbnail = GalleryItemThumbnail(
            m_resourceThumbnailGallery, m_resourceThumbnailDrag);
        resGalItem.Label = tr("Resource");
        resGalItem.Action = m_addResourceAction;
        resGalItem.DragData = m_resourceMimeData;

        GalleryItem wrkGalItem;
        wrkGalItem.Thumbnail = GalleryItemThumbnail(
            m_workerThumbnailGallery, m_workerThumbnailDrag);
        wrkGalItem.Label = tr("Worker");
        wrkGalItem.Action = m_addWorkerAction;
        wrkGalItem.DragData = m_workerMimeData;

        ret.append(artGalItem);
        ret.append(resGalItem);
        ret.append(wrkGalItem);
    }
    else if (group == tr("Relations"))
    {
        GalleryItem aggrGalItem;
        aggrGalItem.Thumbnail = GalleryItemThumbnail(
            m_aggregationThumbnailGallery, QPixmap(),
            m_aggregationThumbnailConnect);
        aggrGalItem.Label = tr("Aggregation");
        aggrGalItem.Action = m_createAggregationAction;
        aggrGalItem.ConnectData = new ConnectionData(CONNECTION_AGGREGATION);

        GalleryItem asocGalItem;
        asocGalItem.Thumbnail = GalleryItemThumbnail(
            m_associationThumbnailGallery, QPixmap(),
            m_associationThumbnailConnect);
        asocGalItem.Label = tr("Association");
        asocGalItem.Action = m_createAssociationAction;
        asocGalItem.ConnectData = new ConnectionData(CONNECTION_ASSOCIATION);

        GalleryItem compGalItem;
        compGalItem.Thumbnail = GalleryItemThumbnail(
            m_compositionThumbnailGallery, QPixmap(),
            m_compositionThumbnailConnect);
        compGalItem.Label = tr("Composition");
        compGalItem.Action = m_createCompositionAction;
        compGalItem.ConnectData = new ConnectionData(CONNECTION_COMPOSITION);

        GalleryItem depeGalItem;
        depeGalItem.Thumbnail = GalleryItemThumbnail(
            m_dependencyThumbnailGallery, QPixmap(),
            m_dependencyThumbnailConnect);
        depeGalItem.Label = tr("Dependency");
        depeGalItem.Action = m_createDependencyAction;
        depeGalItem.ConnectData = new ConnectionData(CONNECTION_DEPENDENCY);

        GalleryItem geneGalItem;
        geneGalItem.Thumbnail = GalleryItemThumbnail(
            m_generalizationThumbnailGallery, QPixmap(),
            m_generalizationThumbnailConnect);
        geneGalItem.Label = tr("Generalization");
        geneGalItem.Action = m_createGeneralizationAction;
        geneGalItem.ConnectData = new ConnectionData(
            CONNECTION_GENERALIZATION);

        GalleryItem realGalItem;
        realGalItem.Thumbnail = GalleryItemThumbnail(
            m_realizationThumbnailGallery, QPixmap(),
            m_realizationThumbnailConnect);
        realGalItem.Label = tr("Realization");
        realGalItem.Action = m_createRealizationAction;
        realGalItem.ConnectData = new ConnectionData(
            CONNECTION_REALIZATION);

        ret.append(aggrGalItem);
        ret.append(asocGalItem);
        ret.append(compGalItem);
        ret.append(depeGalItem);
        ret.append(geneGalItem);
        ret.append(realGalItem);
    }
    else
    {
        // do nothing
    }

    return ret;
}

IEditor* SwpResourceEditor::copy() const
{
    return new SwpResourceEditor();
}

bool SwpResourceEditor::eventFilter(QObject* watched, QEvent* event)
{
    bool ret = false;

    if (watched == m_view->viewport())
    {
        if (event->type() == QEvent::DragEnter)
        {
            dragEnterEvent(dynamic_cast<QDragEnterEvent* >(event));
            ret = true;
        }
        else if (event->type() == QEvent::DragMove)
        {
            dragMoveEvent(dynamic_cast<QDragMoveEvent* >(event));
            ret = true;
        }
        else if (event->type() == QEvent::Drop)
        {
            dropEvent(dynamic_cast<QDropEvent* >(event));
            ret = true;
        }
        else
        {
            ret = AbstractGraphicEditor::eventFilter(watched, event);
        }
    }
    else
    {
        ret = AbstractGraphicEditor::eventFilter(watched, event);
    }

    return ret;
}

void SwpResourceEditor::dragEnterEvent(QDragEnterEvent* event)
{
    if (event->mimeData()->hasFormat(ACCEPTED_MIMETYPE))
    {
        event->acceptProposedAction();
    }
}

void SwpResourceEditor::dragMoveEvent(QDragMoveEvent* event)
{
    if (event->mimeData()->hasFormat(ACCEPTED_MIMETYPE))
    {
        event->acceptProposedAction();
    }
}

void SwpResourceEditor::dropEvent(QDropEvent* event)
{
    if (event->mimeData()->hasFormat(ACCEPTED_MIMETYPE))
    {
        const QString data = QString(event->mimeData()->data(
        ACCEPTED_MIMETYPE));

        if (data == ARTIFACT_MIMETYPE_DATA)
        {
            createNewArtifact(event->pos());
            event->acceptProposedAction();
        }
        else if (data == RESOURCE_MIMETYPE_DATA)
        {
            createNewResource(event->pos());
            event->acceptProposedAction();
        }
        else if (data == WORKER_MIMETYPE_DATA)
        {
            createNewWorker(event->pos());
            event->acceptProposedAction();

        }
        else
        {
            // do nothing
        }
    }
}

void SwpResourceEditor::createThumbnails()
{
    SwpArtifactGraphicItem artifact("");
    SwpResourceGraphicItem resource("");
    SwpWorkerGraphicItem worker("");

    SwpAggregationGraphicItem aggregation(QPointF(0, 0), QPointF(80, 0));
    SwpAssociationGraphicItem association(QPointF(0, 0), QPointF(80, 0));
    SwpCompositionGraphicItem composition(QPointF(0, 0), QPointF(80, 0));
    SwpDependencyGraphicItem dependency(QPointF(0, 0), QPointF(80, 0));
    SwpGeneralizationGraphicItem generalization(QPointF(0, 0),
        QPointF(80, 0));
    SwpRealizationGraphicItem realization(QPointF(0, 0), QPointF(80, 0));

    m_artifactThumbnailDrag = artifact.thumbnail(DragAndDropRole,
        PREFERRED_THUMBNAIL_SIZE);
    m_artifactThumbnailGallery = artifact.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);

    m_resourceThumbnailDrag = resource.thumbnail(DragAndDropRole,
        PREFERRED_THUMBNAIL_SIZE);
    m_resourceThumbnailGallery = resource.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);

    m_workerThumbnailDrag = worker.thumbnail(DragAndDropRole,
        PREFERRED_THUMBNAIL_SIZE);
    m_workerThumbnailGallery = worker.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);

    m_aggregationThumbnailGallery = aggregation.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);
    //m_aggregationThumbnailConnect = aggregation.connectCursor(
      //  PREFERRED_CURSOR_SIZE);

    m_associationThumbnailGallery = association.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);
    //m_associationThumbnailConnect = association.connectCursor(
      //  PREFERRED_CURSOR_SIZE);

    m_compositionThumbnailGallery = composition.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);
    //m_compositionThumbnailConnect = composition.connectCursor(
      //  PREFERRED_CURSOR_SIZE);

    m_dependencyThumbnailGallery = dependency.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);
    //m_dependencyThumbnailConnect = dependency.connectCursor(
      //  PREFERRED_CURSOR_SIZE);

    m_generalizationThumbnailGallery = generalization.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);
    //m_generalizationThumbnailConnect = generalization.connectCursor(
      //  PREFERRED_CURSOR_SIZE);

    m_realizationThumbnailGallery = realization.thumbnail(GalleryRole,
        PREFERRED_GAL_ITEM_ICON_SIZE);
    //m_realizationThumbnailConnect = realization.connectCursor(
      //  PREFERRED_CURSOR_SIZE);
}

void SwpResourceEditor::createMimeData()
{
    m_artifactMimeData = new QMimeData();
    m_artifactMimeData->setData(ACCEPTED_MIMETYPE,
        ARTIFACT_MIMETYPE_DATA.toAscii());

    m_resourceMimeData = new QMimeData();
    m_resourceMimeData->setData(ACCEPTED_MIMETYPE,
        RESOURCE_MIMETYPE_DATA.toAscii());

    m_workerMimeData = new QMimeData();
    m_workerMimeData->setData(ACCEPTED_MIMETYPE,
        WORKER_MIMETYPE_DATA.toAscii());
}

void SwpResourceEditor::createActions()
{
    m_addArtifactAction = new QAction(this);
    m_addResourceAction = new QAction(this);
    m_addWorkerAction = new QAction(this);

    m_createAggregationAction = new QAction(this);
    m_createAssociationAction = new QAction(this);
    m_createCompositionAction = new QAction(this);
    m_createDependencyAction = new QAction(this);
    m_createGeneralizationAction = new QAction(this);
    m_createRealizationAction = new QAction(this);
}

void SwpResourceEditor::connectSlots()
{
    connect(m_addArtifactAction, SIGNAL(triggered()),
        this, SLOT(createNewArtifact()));
    connect(m_addResourceAction, SIGNAL(triggered()),
        this, SLOT(createNewResource()));
    connect(m_addWorkerAction, SIGNAL(triggered()),
        this, SLOT(createNewWorker()));

    connect(m_createAggregationAction, SIGNAL(triggered()),
        this, SLOT(beginAggregation()));
    connect(m_createAssociationAction, SIGNAL(triggered()),
        this, SLOT(beginAssociation()));
    connect(m_createCompositionAction, SIGNAL(triggered()),
        this, SLOT(beginComposition()));
    connect(m_createDependencyAction, SIGNAL(triggered()),
        this, SLOT(beginDependency()));
    connect(m_createGeneralizationAction, SIGNAL(triggered()),
        this, SLOT(beginGeneralization()));
    connect(m_createRealizationAction, SIGNAL(triggered()),
        this, SLOT(beginRealization()));
}

// private slots
void SwpResourceEditor::createNewArtifact(QPoint where)
{
    setModeToSelect();

    QPointF _where;

    if (where.isNull())
    {
        _where = m_view->mapToScene(m_view->viewport()->rect().center());
    }
    else
    {
        _where = m_view->mapToScene(where);
    }

    SwpArtifactGraphicItem* item = new SwpArtifactGraphicItem("");
    const QRectF rect = item->boundingRect();
    item->setPos(int(_where.x() - rect.width() / 2),
        int(_where.y() - rect.height() / 2));

    AddGraphicItemCommand* addCommand = new AddGraphicItemCommand(
        m_documentData, item);

    UndoManager::getInstance()->push(m_document->data(), addCommand);

    m_view->setFocus();
}

void SwpResourceEditor::createNewResource(QPoint where)
{
    setModeToSelect();

    QPointF _where;

    if (where.isNull())
    {
        _where = m_view->mapToScene(m_view->viewport()->rect().center());
    }
    else
    {
        _where = m_view->mapToScene(where);
    }

    SwpResourceGraphicItem* item = new SwpResourceGraphicItem("");
    const QRectF rect = item->boundingRect();
    item->setPos(int(_where.x() - rect.width() / 2),
        int(_where.y() - rect.height() / 2));

    AddGraphicItemCommand* addCommand = new AddGraphicItemCommand(
        m_documentData, item);

    UndoManager::getInstance()->push(m_document->data(), addCommand);

    m_view->setFocus();
}

void SwpResourceEditor::createNewWorker(QPoint where)
{
    setModeToSelect();

    QPointF _where;

    if (where.isNull())
    {
        _where = m_view->mapToScene(m_view->viewport()->rect().center());
    }
    else
    {
        _where = m_view->mapToScene(where);
    }

    SwpWorkerGraphicItem* item = new SwpWorkerGraphicItem("");
    const QRectF rect = item->boundingRect();
    item->setPos(int(_where.x() - rect.width() / 2),
        int(_where.y() - rect.height() / 2));

    AddGraphicItemCommand* addCommand = new AddGraphicItemCommand(
        m_documentData, item);

    UndoManager::getInstance()->push(m_document->data(), addCommand);

    m_view->setFocus();
}

void SwpResourceEditor::beginAggregation()
{
    setModeToConnect(ConnectionData(CONNECTION_AGGREGATION, 0));
}

void SwpResourceEditor::beginAssociation()
{
    setModeToConnect(ConnectionData(CONNECTION_ASSOCIATION, 0));
}

void SwpResourceEditor::beginComposition()
{
    setModeToConnect(ConnectionData(CONNECTION_COMPOSITION, 0));
}

void SwpResourceEditor::beginDependency()
{
    setModeToConnect(ConnectionData(CONNECTION_DEPENDENCY, 0));
}

void SwpResourceEditor::beginGeneralization()
{
    setModeToConnect(ConnectionData(CONNECTION_GENERALIZATION, 0));
}

void SwpResourceEditor::beginRealization()
{
    setModeToConnect(ConnectionData(CONNECTION_REALIZATION, 0));
}
